为一块新的LCD屏编写了驱动,然后编译进内核,下载到开发板上,这时候,屏幕也许除了背光,没有其它任何的显示,这个时候,我们应该怎么进行调试呢?下面是几个最基本的问题:
- 如何确定LCD驱动加载了呢?
- LCD驱动的基本信息是否正确呢,如何检查?
- 能否通过应用程序来对LCD屏幕进行控制呢?这里的FrameBuffer的应用程序如何编写呢?
下面是一些我现在用到的办法:
命令行查看FrameBuffer的信息
# cat /proc/fb //在/proc目录,查看当前系统中的帧缓冲设备 0 jz-lcd 1 jz-slcd # ls /dev/fb* //查看/dev目录下的不同的帧缓冲设备 /dev/fb0 /dev/fb1
可以看到,现在系统中有两个帧缓冲设备。其中第0个帧缓存设备/dev/fb0,对应的是LCD控制器(jz-lcd)的驱动,而第1个帧缓存设备/dev/fb1,就是智能LCD控制器(jz-slcd)的驱动。可以在./drivers/video/目录下面的驱动文件(这里是jzlcd.c文件和jz4740_slcd.c文件)中看到/proc/fb中命名和注册。
static struct lcd_cfb_info * jzfb_alloc_fb_info(void) { …… strcpy(cfb->fb.fix.id, "jz-lcd"); //这里的字符串"jz-lcd"就是在/proc/fb中看到的打印信息,修改这里可以在/proc/fb中看到相应的改动 …… } static int __init jzfb_init(void) { …… err = register_framebuffer(&cfb->fb); if (err < 0) { dprintk("jzfb_init(): register framebuffer err.\n"); goto failed; } …… }
register_framebuffer()函数就是将LCD控制器(jz-lcd)的驱动注册到frame buffer中,这样才有我们看到的/proc/fb中的帧缓存设备0 jz-lcd以及/dev/fb0设备描述符。同理,我们还可以同时将智能LCD控制器(jz-slcd)的驱动注册到frame buffer中。
可以看到,frame buffer作为驱动程序,是各种不同的显示设备对OS,应用层的一个统一的接口,一个驱动接口。
在刚开始的时候,由于没有在.config文件中将jz-lcd驱动选项去掉,所以加载了两个帧缓冲设备,如上面所示。
命令行查看FrameBuffer的中断信息
查看到帧设备之后,那么帧设备有没有工作呢?我们可以通过产看中断的办法,大概的看到帧设备是否工作。
# cat /proc/interrupts CPU0 3: 2 INTC ohci_hcd:usb1 9: 5758 INTC serial 14: 0 INTC MMC/SD 15: 0 INTC rtc 17: 0 INTC cim 23: 73137 INTC jz-timerirq 30: 0 INTC lcd 32: 0 DMA auto 33: 0 DMA MMC Rx 34: 0 DMA MMC Tx 35: 0 DMA audio adc 36: 0 DMA audio dac 150: 0 GPIO udc_pnp 158: 0 GPIO MMC card detect 173: 0 GPIO poweroff ERR: 0
通过打印/proc/interrupts的信息,可以看到30号中断,对应上面的jz-lcd驱动,而32号中断,是DMA中断,名字为auto,这其实对应jz-slcd的中断,jz4740_slcd.c文件中有下面的代码为证:
static int slcd_dma_init(void) { /* Request DMA channel and setup irq handler */ dma_chan = jz_request_dma(DMA_ID_AUTO, "auto", slcd_dma_irq, 0, NULL); if (dma_chan < 0) { printk("Request DMA Failed\n"); return -1; } printk("DMA channel %d is requested by SLCD!\n", dma_chan); …… }
可以看到,这驱动中,通过jz_request_dma()函数请求可一个DMA通道,名字为auto,并且注册了DMA中断函数slcd_dma_irq()。通过查看内核启动信息,也可以验证这一点。
# cat /proc/ …… DMA channel 0 is requested by SLCD! ……
其中,cat /proc/interrupts的输出第二列表示进入这个中断的次数。可以看到,进入jz-lcd和auto的中断次数为0次,也就是从没有进入过中断。没有进入过中断,就没有DMA将图像从内存写入显存(GRAM)的过程,LCD上没有图像显示,也就不足为奇了。
查看内核启动信息
查看内核启动时的输出信息是最直接,有效的调试方式。可以在命令行下使用klog命令,也可以直接接串口获得内核启动信息。下面就下面的输出做出分析:
fb_alloc_cmap,fb.cmap.len:256.... dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:0,RGBt:(0,0,0,65535) dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:1,RGBt:(0,0,43690,65535) …… //省略了regno:2~regno:253 dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:254,RGBt:(44767,65503,65519,65535) dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:255,RGBt:(65535,65535,65535,65535) jzfb_set_var: after fb_set_cmap... slcd_hw_init---------jzfb.w:240jzfb.h:320,jzfb.bpp:16 in order to init slcd SLCD_CFG=0x4400 SLCDC: PixClock:16000000 LcdClock:28000000 SLCD_CFG=0x4400 jzfb_set_pardbg::drivers/video/jz4740_slcd.c,LINE(460): var.yoffset: 0 dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:0,RGBt:(0,0,0,65535) dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:1,RGBt:(0,0,43690,65535) …… //省略了regno:2~regno:13 dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:14,RGBt:(65535,65535,21845,65535) dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:15,RGBt:(65535,65535,65535,65535) Console: switching to colour frame buffer device 30x40 dbg::drivers/video/jz4740_slcd.c,LINE(460): var.yoffset: 0 dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:0,RGBt:(0,0,0,65535) dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:1,RGBt:(0,0,43690,65535) …… //省略了regno:2~regno:13 dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:14,RGBt:(65535,65535,21845,65535) dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:15,RGBt:(65535,65535,65535,65535) dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:0,RGBt:(0,0,0,65535) dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:1,RGBt:(0,0,43690,65535) …… //省略了regno:2~regno:13 dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:14,RGBt:(65535,65535,21845,65535) dbg::drivers/video/jz4740_slcd.c,LINE(221): regno:15,RGBt:(65535,65535,65535,65535) fb0: jz-ny-slcd frame buffer device, using 256K of video memory DMA channel 0 is requested by SLCD!
之所以有上面这么多的输出信息,是因为驱动文件中原来定义的调试宏被打开了,并且,你也可以在你自己觉得有疑问,需要打印的地方加入输出信息。 打开调试宏的办法: 例如在文件jz4740_slcd.c中,
//#undef DEBUG //如果不想打印调试信息,那么就使用这一句 #define DEBUG //如果想打印调试信息,使用这一句 #ifdef DEBUG #define dprintk(x...) printk(x) #else #define dprintk(x...) #endif
原来#define DEBUG是被注释掉的,现在去掉注释,然后将#undef DEBUG注释掉。这样,下面文件中使用到的dprintk()函数就会使用printk()函数做内核的输出答应。
至于添加答应操作,那么就使用dprintk()函数就可以了,用法和printf()类似。